package com.yth.utils;

import com.alibaba.fastjson.JSON;
import lombok.Getter;
import okhttp3.*;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Array;
import java.lang.reflect.Type;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;

/**
 * http帮助类
 *
 * @author yutianhong
 * @version 1.0
 * @since 2024/12/19 14:08
 */
public class HttpHelper {
    private static final MediaType JSON_MEDIA_TYPE = MediaType.get("application/json");
    private static final HttpHelper DEFAULT_INSTANCE = new HttpHelper(HttpHelper.HttpConfig.builder().build());

    private final HttpConfig config;
    private final OkHttpClient client;

    public static HttpHelper getInstance() {
        return DEFAULT_INSTANCE;
    }

    private HttpHelper(HttpConfig config) {
        this.config = config;
        this.client = createClient(config);
    }

    /**
     * 创建自定义配置的实例
     */
    public static HttpHelper create(HttpHelper.HttpConfig config) {
        return new HttpHelper(config);
    }

    /**
     * 创建请求构建器
     */
    public HttpHelper.RequestBuilder request() {
        return new HttpHelper.RequestBuilder(this);
    }

    // ==========静态便捷方法 ==========

    /**
     * 发送GET请求
     */
    public static String get(String url) {
        return DEFAULT_INSTANCE.request().url(url).get().execute().asString();
    }

    /**
     * 发送带URL参数的GET请求
     */
    public static String get(String url, Map<String, ?> urlParam) {
        return DEFAULT_INSTANCE.request().url(url).addUrlParams(urlParam).get().execute().asString();
    }

    /**
     * 发送带URL参数和请求头的GET请求
     */
    public static String get(String url, Map<String, ?> urlParam, Map<String, String> headers) {
        return DEFAULT_INSTANCE.request()
                .url(url).addUrlParams(urlParam).addHeaders(headers).get()
                .execute().asString();
    }

    /**
     * 快速POST JSON请求
     */
    public static String postJson(String url, String body) {
        return DEFAULT_INSTANCE.request().url(url).jsonBody(body).post().execute().asString();
    }

    /**
     * 发送带请求头的JSON格式POST请求
     */
    public static String postJson(String url, String bodyParam, Map<String, String> headers) {
        return DEFAULT_INSTANCE.request().url(url).jsonBody(bodyParam).addHeaders(headers).post().execute().asString();
    }

    /**
     * 快速POST表单请求
     */
    public static String postForm(String url, Map<String, ?> formData) {
        return DEFAULT_INSTANCE.request().url(url).formBody(formData).post().execute().asString();
    }

    public static class HttpConfig {
        private final Duration connectTimeout;
        private final Duration readTimeout;
        private final Duration writeTimeout;
        private final boolean trustAllCerts;
        private final JsonHandler jsonHandler;

        public HttpConfig(Builder builder) {
            this.connectTimeout = builder.connectTimeout;
            this.readTimeout = builder.readTimeout;
            this.writeTimeout = builder.writeTimeout;
            this.trustAllCerts = builder.trustAllCerts;
            this.jsonHandler = builder.jsonHandler;
        }

        public static Builder builder() {
            return new Builder();
        }

        public static class Builder {
            private Duration connectTimeout = Duration.ofSeconds(3);
            private Duration readTimeout = Duration.ofMinutes(2);
            private Duration writeTimeout = Duration.ofMinutes(2);
            private boolean trustAllCerts = true;
            private JsonHandler jsonHandler = new JsonHandler() {
                @Override
                public String toString(Object object) {
                    return JSON.toJSONString(object);
                }

                @Override
                public Object parse(String jsonStr) {
                    return JSON.parse(jsonStr);
                }

                @Override
                public <T> T parse(String jsonStr, Class<T> type) {
                    return JSON.parseObject(jsonStr, type);
                }

                @Override
                public <T> T parse(String jsonStr, Type objectType) {
                    return JSON.parseObject(jsonStr, objectType);
                }
            };

            public Builder connectTimeout(Duration timeout) {
                this.connectTimeout = timeout;
                return this;
            }

            public Builder readTimeout(Duration timeout) {
                this.readTimeout = timeout;
                return this;
            }

            public Builder writeTimeout(Duration timeout) {
                this.writeTimeout = timeout;
                return this;
            }

            public Builder trustAllCerts(boolean trustAllCerts) {
                this.trustAllCerts = trustAllCerts;
                return this;
            }

            public Builder jsonHandler(JsonHandler jsonHandler) {
                this.jsonHandler = jsonHandler;
                return this;
            }

            public HttpConfig build() {
                return new HttpConfig(this);
            }
        }
    }

    /**
     * JSON处理器接口，用于自定义JSON序列化和反序列化实现
     */
    public interface JsonHandler {
        /**
         * 将对象转换为JSON字符串
         */
        String toString(Object object);

        /**
         * 解析JSON字符串为对象
         */
        Object parse(String jsonStr);

        /**
         * 解析JSON字符串为指定类型的对象
         */
        <T> T parse(String jsonStr, Class<T> type);

        /**
         * 解析JSON字符串为指定泛型类型的对象
         */
        <T> T parse(String jsonStr, Type objectType);
    }

    /**
     * 请求构建器
     */
    public static class RequestBuilder {
        private final HttpHelper httpHelper;
        private String url;
        private final Map<String, String> headers = new HashMap<>();
        private final Map<String, Object> queryParams = new HashMap<>();
        private RequestBody body = RequestBody.create(JSON_MEDIA_TYPE, "");
        private String method = "GET";
        private boolean encodePathSegments = false;

        private RequestBuilder(HttpHelper httpHelper) {
            this.httpHelper = httpHelper;
        }

        public HttpHelper.RequestBuilder url(String url) {
            if (url == null || url.trim().isEmpty()) {
                throw new IllegalArgumentException("URL cannot be null or empty");
            }
            this.url = url;
            return this;
        }

        public HttpHelper.RequestBuilder addHeader(String name, String value) {
            this.headers.put(name, value);
            return this;
        }

        public HttpHelper.RequestBuilder addHeaders(Map<String, String> headers) {
            if (headers != null) {
                this.headers.putAll(headers);
            }
            return this;
        }

        public HttpHelper.RequestBuilder addUrlParam(String name, Object value) {
            this.queryParams.put(name, value);
            return this;
        }

        public HttpHelper.RequestBuilder addUrlParams(Map<String, ?> params) {
            if (params != null) {
                this.queryParams.putAll(params);
            }
            return this;
        }

        public HttpHelper.RequestBuilder jsonBody(String jsonStr) {
            this.body = RequestBody.create(JSON_MEDIA_TYPE, jsonStr);
            return this;
        }

        public HttpHelper.RequestBuilder formBody(Map<String, ?> formData) {
            FormBody.Builder formBuilder = new FormBody.Builder();
            if (formData != null) {
                List<String> params = transferParam(formData, httpHelper.config.jsonHandler);
                for (int i = 0; i < params.size(); i += 2) {
                    formBuilder.add(params.get(i), params.get(i + 1));
                }
            }
            this.body = formBuilder.build();
            return this;
        }

        public HttpHelper.RequestBuilder encodePathSegments() {
            this.encodePathSegments = true;
            return this;
        }

        public HttpHelper.RequestBuilder get() {
            this.method = "GET";
            return this;
        }

        public HttpHelper.RequestBuilder post() {
            this.method = "POST";
            return this;
        }

        public HttpHelper.RequestBuilder put() {
            this.method = "PUT";
            return this;
        }

        public HttpHelper.RequestBuilder delete() {
            this.method = "DELETE";
            return this;
        }

        /**
         * 同步执行请求
         */
        public HttpHelper.HttpResponse execute() {
            try {
                Request request = buildRequest();
                return this.executeRequest(request);
            } catch (Exception e) {
                throw new HttpHelper.HttpException("Request execution failed", e);
            }
        }

        /**
         * 异步执行请求
         */
        public CompletableFuture<HttpHelper.HttpResponse> executeAsync() {
            return CompletableFuture.supplyAsync(this::execute);
        }

        /**
         * 异步执行请求
         */
        public <T> CompletableFuture<HttpHelper.HttpResponse> executeAsync(Executor executor) {
            return CompletableFuture.supplyAsync(this::execute, executor);
        }

        /**
         * 流式处理SSE（Server-Sent Events）格式的响应
         *
         * @param responseHandler 流式处理器
         */
        public void executeSSEStream(SSEResponseHandler responseHandler) {
            Objects.requireNonNull(responseHandler, "responseHandler cannot be null");
            Request request = buildRequest();
            httpHelper.client.newCall(request).enqueue(new SSECallback(responseHandler));
        }

        private static class SSECallback implements Callback {

            private final SSEResponseHandler responseHandler;

            SSECallback(SSEResponseHandler responseHandler) {
                this.responseHandler = Objects.requireNonNull(responseHandler);
            }

            @Override
            public void onFailure(Call call, IOException e) {
                responseHandler.onError(e);
            }

            @Override
            public void onResponse(Call call, Response response) {
                if (!response.isSuccessful()) {
                    responseHandler.onError(new HttpHelper.HttpException("HTTP " + response.code() + ": " + response.message()));
                    return;
                }
                try (BufferedReader reader = new BufferedReader(
                        new InputStreamReader(response.body().byteStream(), StandardCharsets.UTF_8))) {
                    String line;
                    SSEEvent.Builder eventBuilder = new SSEEvent.Builder();
                    List<String> dataLines = new ArrayList<>();
                    while ((line = reader.readLine()) != null) {
                        if (line.isEmpty()) {        // 空行- 事件结束
                            SSEEvent event = eventBuilder.build(dataLines);
                            if (!event.isEmpty()) {
                                responseHandler.onEvent(event);
                            }
                            // 重置构建器
                            eventBuilder = new SSEEvent.Builder();
                            dataLines.clear();
                        } else {
                            // 解析字段
                            parseSSELine(line, eventBuilder, dataLines);
                        }
                    }

                    // 处理最后一个事件（如果流没有以空行结束）
                    SSEEvent lastEvent = eventBuilder.build(dataLines);
                    if (!lastEvent.isEmpty()) {
                        responseHandler.onEvent(lastEvent);
                    }

                    responseHandler.onComplete();
                } catch (Throwable e) {
                    responseHandler.onError(e);
                }
            }

            private void parseSSELine(String line, SSEEvent.Builder builder, List<String> dataLines) {
                if (line.startsWith(":")) {
                    return; // 注释行，忽略
                }
                int colonIndex = line.indexOf(':');
                if (colonIndex == -1) {
                    return; // 无效行
                }

                String field = line.substring(0, colonIndex);
                String value = colonIndex + 1 < line.length() ? line.substring(colonIndex + 1) : "";

                // 移除值开头的单个空格（SSE 规范）
                if (value.startsWith(" ")) {
                    value = value.substring(1);
                }

                switch (field) {
                    case "data":
                        dataLines.add(value);
                        break;
                    case "event":
                        builder.event(value);
                        break;
                    case "id":
                        builder.id(value);
                        break;
                    case "retry":
                        try {
                            builder.retry(Integer.parseInt(value));
                        } catch (NumberFormatException ignored) {
                            //忽略无效的retry 值
                        }
                        break;
                }
            }
        }

        private HttpHelper.HttpResponse executeRequest(Request request) throws IOException {
            Response response = httpHelper.client.newCall(request).execute();
            if (!response.isSuccessful()) {
                throw new HttpHelper.HttpException("{protocol=" + response.protocol()
                        + ", code=" + response.code()
                        + ", message=" + response.message()
                        + ", url=" + request.url()
                        + '}');
            }
            return new HttpHelper.HttpResponse(response, httpHelper.config.jsonHandler);
        }

        private Request buildRequest() {
            if (url == null) {
                throw new IllegalStateException("URL is required");
            }
            HttpUrl httpUrl = buildUrl();
            Request.Builder builder = new Request.Builder().url(httpUrl);
            // 添加headers
            if (!headers.isEmpty()) {
                builder.headers(Headers.of(headers));
            }
            // 设置method和body
            switch (method.toUpperCase()) {
                case "GET":
                    builder.get();
                    break;
                case "POST":
                    builder.post(body);
                    break;
                case "PUT":
                    builder.put(body);
                    break;
                case "DELETE":
                    builder.delete(body);
                    break;
                default:
                    throw new IllegalArgumentException("Unsupported method: " + method);
            }

            return builder.build();
        }

        private HttpUrl buildUrl() {
            HttpUrl originalUrl = HttpUrl.parse(url);
            if (originalUrl == null) {
                throw new IllegalArgumentException("Invalid URL: " + url);
            }
            HttpUrl.Builder urlBuilder;
            if (encodePathSegments) {
                urlBuilder = new HttpUrl.Builder()
                        .scheme(originalUrl.scheme())
                        .encodedUsername(originalUrl.encodedUsername())
                        .encodedPassword(originalUrl.encodedPassword())
                        .host(originalUrl.host()).port(originalUrl.port()).encodedQuery(originalUrl.encodedQuery())
                        .encodedFragment(originalUrl.encodedFragment());
                for (String pathSegment : originalUrl.pathSegments()) {
                    try {
                        urlBuilder.addEncodedPathSegment(URLEncoder.encode(pathSegment, StandardCharsets.UTF_8.name()));
                    } catch (UnsupportedEncodingException e) {
                        throw new HttpException("Failed to encode path segment: " + pathSegment, e);
                    }
                }
            } else {
                urlBuilder = originalUrl.newBuilder();
            }
            // 添加查询参数
            if (!queryParams.isEmpty()) {
                List<String> params = transferParam(queryParams, httpHelper.config.jsonHandler);
                for (int i = 0; i < params.size(); i += 2) {
                    urlBuilder.addQueryParameter(params.get(i), params.get(i + 1));
                }
            }

            return urlBuilder.build();
        }
    }

    @Getter
    public static class SSEEvent {
        private final String data;
        private final String event;
        private final String id;
        private final Integer retry;

        public SSEEvent(String data, String event, String id, Integer retry) {
            this.data = data;
            this.event = event;
            this.id = id;
            this.retry = retry;
        }

        public boolean isEmpty() {
            return data == null && event == null && id == null && retry == null;
        }

        public static class Builder {
            private String event;
            private String id;
            private Integer retry;

            public Builder event(String event) {
                this.event = event;
                return this;
            }

            public Builder id(String id) {
                this.id = id;
                return this;
            }

            public Builder retry(Integer retry) {
                this.retry = retry;
                return this;
            }

            public SSEEvent build(List<String> dataLines) {
                // 合并多行data
                String data = dataLines.isEmpty() ? null : String.join("\n", dataLines);
                return new SSEEvent(data, event, id, retry);
            }
        }
    }

    /**
     * 流式响应处理器接口
     */
    public interface SSEResponseHandler {
        /**
         * 接收到完整的 SSE 事件
         */
        void onEvent(SSEEvent event);

        /**
         * 响应完成时调用
         */
        void onComplete();

        /**
         * 发生错误时调用
         * * @param throwable 异常信息
         */
        void onError(Throwable throwable);
    }

    /**
     * HTTP响应包装类
     */
    public static class HttpResponse {
        private final Response response;
        private final JsonHandler jsonHandler;

        HttpResponse(Response response, JsonHandler jsonHandler) {
            this.response = response;
            this.jsonHandler = jsonHandler;
        }

        public String asString() {
            try (ResponseBody body = response.body()) {
                return body == null ? null : body.string();
            } catch (IOException e) {
                throw new HttpException("Failed to read response body", e);
            }
        }

        public <T> T as(Class<T> objectClass) {
            String responseStr = asString();
            if (responseStr == null) {
                return null;
            }
            if (objectClass == String.class) {
                return objectClass.cast(responseStr);
            }
            return jsonHandler.parse(responseStr, objectClass);
        }

        public <T> T as(Type objectType) {
            String responseStr = asString();
            if (responseStr == null) {
                return null;
            }
            return jsonHandler.parse(responseStr, objectType);
        }
    }

    /**
     * HTTP异常类
     */
    public static class HttpException extends RuntimeException {
        public HttpException(String message) {
            super(message);
        }

        public HttpException(String message, Throwable cause) {
            super(message, cause);
        }
    }

    private OkHttpClient createClient(HttpConfig config) {
        OkHttpClient.Builder builder = new OkHttpClient.Builder()
                .connectTimeout(config.connectTimeout)
                .readTimeout(config.readTimeout)
                .writeTimeout(config.writeTimeout);
        if (config.trustAllCerts) {
            try {
                // Create a trust manager that does not validate certificate chains
                final TrustManager[] trustAllCerts = new TrustManager[]{
                        new X509TrustManager() {
                            @Override
                            public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) {
                            }

                            @Override
                            public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) {
                            }

                            @Override
                            public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                                return new java.security.cert.X509Certificate[]{};
                            }
                        }
                };
                // Install the all-trusting trust manager
                final SSLContext sslContext = SSLContext.getInstance("SSL");
                sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
                // Create a ssl socket factory with our all-trusting manager
                SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();

                builder.sslSocketFactory(sslSocketFactory, (X509TrustManager) trustAllCerts[0])
                        .hostnameVerifier((hostname, session) -> true);
            } catch (Exception e) {
                throw new HttpException("Failed to create SSL context", e);
            }
        }
        return builder.build();
    }

    private static List<String> transferParam(Map<String, ?> source, JsonHandler jsonHandler) {
        if (source == null || source.isEmpty()) {
            return Collections.emptyList();
        }
        List<String> params = new ArrayList<>((source.size() << 1));
        for (Map.Entry<String, ?> entry : source.entrySet()) {
            Object value = entry.getValue();
            if (value == null) {
                continue;
            }
            if (value instanceof Number || value instanceof Boolean || value instanceof CharSequence) {
                params.add(entry.getKey());
                params.add(value.toString());
            } else if (value instanceof Collection) {
                Collection<?> collection = (Collection<?>) value;
                for (Object object : collection) {
                    params.add(entry.getKey());
                    params.add(handleObject(object, jsonHandler));
                }
            } else if (value.getClass().isArray()) {
                int length = Array.getLength(value);
                for (int i = 0; i < length; i++) {
                    params.add(entry.getKey());
                    params.add(handleObject(Array.get(value, i), jsonHandler));
                }
            } else {
                params.add(entry.getKey());
                params.add(jsonHandler.toString(value));
            }
        }
        return params;
    }

    private static String handleObject(Object o, JsonHandler jsonHandler) {
        if (o instanceof Number || o instanceof Boolean || o instanceof CharSequence) {
            return o.toString();
        }
        return jsonHandler.toString(o);
    }
}
